// Copyright (c) 2010 SuccessFactors, Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
//     * Redistributions of source code must retain the above
//       copyright notice, this list of conditions and the following
//       disclaimer.
//
//     * Redistributions in binary form must reproduce the above
//       copyright notice, this list of conditions and the following
//       disclaimer in the documentation and/or other materials
//       provided with the distribution.
//
//     * Neither the name of the SuccessFactors, Inc. nor the names of
//       its contributors may be used to endorse or promote products
//       derived from this software without specific prior written
//       permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.

package org.owasp.jxt.webinf;

import java.io.IOException;
import java.io.InputStream;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;

/**
 * WebXmlReader
 *
 * @author Jeffrey Ichnowski
 * @version $Revision: 8 $
 */
public final class WebXmlReader {
    static final SAXParserFactory SAX_FACTORY = SAXParserFactory.newInstance();
    static {
        SAX_FACTORY.setNamespaceAware(true);
        SAX_FACTORY.setValidating(true);
    }

    static final PublicId WEB_APP_2_2 = new PublicId(
        "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN",
        "http://java.sun.com/j2ee/dtds/web-app_2_2.dtd");

    static final PublicId WEB_APP_2_3 = new PublicId(
        "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN",
        "http://java.sun.com/dtd/web-app_2_3.dtd");

    SAXParser _parser;
    XMLReader _reader;
    WebApplication _webApp = new WebApplication();

    DefaultHandler _baseHandler = new DefaultHandler() {
        @Override
        public InputSource resolveEntity(String publicId, String systemId)
            throws IOException, SAXException
        {
            PublicId dtd;

            if (WEB_APP_2_3.matches(publicId, systemId)) {
                dtd = WEB_APP_2_3;
            } else if (WEB_APP_2_2.matches(publicId, systemId)) {
                dtd = WEB_APP_2_2;
            } else {
                throw new SAXException("Unrecognized DTD entity: "+publicId+", "+systemId);
            }

            InputStream in = dtd.resolveLocal(getClass());

            if (in == null) {
                throw new SAXException("Could not locate "+systemId);
            }

            return new InputSource(in);
        }
    };

    static enum WebXmlParseState {
        TOP, WEB_APP, TAGLIB, TAGLIB_URI, TAGLIB_LOCATION, OTHER;
    };

    private ContentHandler createContentHandler() {
        return new XmlHandler<WebXmlParseState>(WebXmlParseState.TOP) {
            String _taglibUri;
            String _taglibLocation;

            @Override
            public WebXmlParseState pushElement(String uri, String localName, String qName, Attributes attrs)
                throws SAXException
            {
                switch (_state) {
                case TOP:
                    if ("web-app".equals(localName)) {
                        return WebXmlParseState.WEB_APP;
                    } else {
                        throw new SAXException("Expected <web-app>, found <"+localName+">");
                    }
                case WEB_APP:
                    if ("taglib".equals(localName)) {
                        _taglibUri = null;
                        _taglibLocation = null;
                        return WebXmlParseState.TAGLIB;
                    } else {
                        return WebXmlParseState.OTHER;
                    }
                case TAGLIB:
                    if ("taglib-uri".equals(localName)) {
                        return WebXmlParseState.TAGLIB_URI;
                    } else if ("taglib-location".equals(localName)) {
                        return WebXmlParseState.TAGLIB_LOCATION;
                    } else {
                        return WebXmlParseState.OTHER;
                    }
                case OTHER:
                    return WebXmlParseState.OTHER;
                default:
                    throw new SAXException("Invalid state: "+_state);
                }
            }

            @Override
            public void popElement(String uri, String localName, String qName, String content)
                throws SAXException
            {
                switch (_state) {
                case TAGLIB:
                    _webApp.addTaglib(_taglibUri, _taglibLocation);
                    break;
                case TAGLIB_URI:
                    _taglibUri = content;
                    break;
                case TAGLIB_LOCATION:
                    _taglibLocation = content;
                    break;
                default:
                    break;
                }
            }
        };
    }


    private WebXmlReader() throws ParserConfigurationException, SAXException {
        _parser = SAX_FACTORY.newSAXParser();
        _reader = _parser.getXMLReader();
        _reader.setEntityResolver(_baseHandler);
        _reader.setErrorHandler(_baseHandler);
        _reader.setDTDHandler(_baseHandler);
        _reader.setContentHandler(createContentHandler());
    }

    public static WebApplication parse(InputStream in)
        throws ParserConfigurationException, SAXException, IOException
    {
        return new WebXmlReader().doParse(new InputSource(in));
    }

    private WebApplication doParse(InputSource in)
        throws SAXException, IOException
    {
        _reader.parse(in);
        return _webApp;
    }

//     public static void main(String[] args) throws Exception {
//         System.out.println(parse(new java.io.FileInputStream(args[0])));
//     }

} // WebXmlReader
